Load and Explore the Data

# load and inspect gapminder data
library(dslabs)
data(gapminder)
head(gapminder)

Life Expectancy and Fertility Rates

# compare infant mortality in Sri Lanka and Turkey
gapminder %>%
    filter(year == 2015 & country %in% c("Sri Lanka", "Turkey")) %>%
    select(country, infant_mortality)
# basic scatterplot of life expectancy versus fertility
ds_theme_set()    # set plot theme
filter(gapminder, year == 1962) %>%
    ggplot(aes(fertility, life_expectancy)) +
    geom_point()

    
# add color as continent
filter(gapminder, year == 1962) %>%
    ggplot(aes(fertility, life_expectancy, color = continent)) +
    geom_point()

Faceting

Faceting makes multiple side-by-side plots stratified by some variable. This is a way to ease comparisons.

The facet_grid() function allows faceting by up to two variables, with rows faceted by one variable and columns faceted by the other variable. To facet by only one variable, use the dot operator as the other variable.

The facet_wrap() function facets by one variable and automatically wraps the series of plots so they have readable dimensions.

Faceting keeps the axes fixed across all plots, easing comparisons between plots.

# facet by continent and year
filter(gapminder, year %in% c(1962, 2012)) %>%
    ggplot(aes(fertility, life_expectancy, col = continent)) +
    geom_point() +
    facet_grid(continent ~ year)


# facet by year only
filter(gapminder, year %in% c(1962, 2012)) %>%
    ggplot(aes(fertility, life_expectancy, col = continent)) +
    geom_point() +
    facet_grid(. ~ year)


# facet by year, plots wrapped onto multiple rows
years <- c(1962, 1980, 1990, 2000, 2012)
continents <- c("Europe", "Asia")
gapminder %>%
    filter(year %in% years & continent %in% continents) %>%
    ggplot(aes(fertility, life_expectancy, col = continent)) +
    geom_point() +
    facet_wrap(~year)

Time Series Plots

The geom_line() geometry connects adjacent data points to form a continuous line. A line plot is appropriate when points are regularly spaced, densely packed and from a single data series.

You can plot multiple lines on the same graph. Remember to group or color by a variable so that the lines are plotted independently.

Labeling is usually preferred over legends. However, legends are easier to make and appear by default. Add a label with geom_text(), specifying the coordinates where the label should appear on the graph.

Single Time Series

# scatterplot of US fertility by year
gapminder %>%
    filter(country == "United States") %>%
    ggplot(aes(year, fertility)) +
    geom_point()


# line plot of US fertility by year
gapminder %>%
    filter(country == "United States") %>%
    ggplot(aes(year, fertility)) +
    geom_line()

Multiple Time Series

# line plot fertility time series for two countries- only one line (incorrect)
countries <- c("South Korea", "Germany")
gapminder %>% filter(country %in% countries) %>%
    ggplot(aes(year, fertility)) +
    geom_line()

    
# line plot fertility time series for two countries - one line per country
gapminder %>% filter(country %in% countries) %>%
    ggplot(aes(year, fertility, group = country)) +
    geom_line()


# fertility time series for two countries - lines colored by country
gapminder %>% filter(country %in% countries) %>%
    ggplot(aes(year, fertility, col = country)) +
    geom_line()

Adding Text Labels to a Plot

# life expectancy time series - lines colored by country and labeled, no legend
labels <- data.frame(country = countries, x = c(1975, 1965), y = c(60, 72))
gapminder %>% filter(country %in% countries) %>%
    ggplot(aes(year, life_expectancy, col = country)) +
    geom_line() +
    geom_text(data = labels, aes(x, y, label = country), size = 5) +
    theme(legend.position = "none")

Transformations

We use GDP data to compute income in US dollars per day, adjusted for inflation.

Log transformations convert multiplicative changes into additive changes. Common transformations are the log base 2 transformation and the log base 10 transformation. The choice of base depends on the range of the data. The natural log is not recommended for visualization because it is difficult to interpret.

The mode of a distribution is the value with the highest frequency. The mode of a normal distribution is the average. A distribution can have multiple local modes.

There are two ways to use log transformations in plots: transform the data before plotting or transform the axes of the plot. Log scales have the advantage of showing the original values as axis labels, while log transformed values ease interpretation of intermediate values between labels.

Scale the x-axis using scale_x_continuous() or scale_x_log10() layers in ggplot2. Similar functions exist for the y-axis.

In 1970, income distribution is bimodal, consistent with the dichotomous Western versus developing worldview.

# add dollars per day variable
gapminder <- gapminder %>%
    mutate(dollars_per_day = gdp/population/365)

# histogram of dollars per day
past_year <- 1970
gapminder %>%
    filter(year == past_year & !is.na(gdp)) %>%
    ggplot(aes(dollars_per_day)) +
    geom_histogram(binwidth = 1, color = "black")


# repeat histogram with log2 scaled data
gapminder %>%
    filter(year == past_year & !is.na(gdp)) %>%
    ggplot(aes(log2(dollars_per_day))) +
    geom_histogram(binwidth = 1, color = "black")

    
# repeat histogram with log2 scaled x-axis
gapminder %>%
    filter(year == past_year & !is.na(gdp)) %>%
    ggplot(aes(dollars_per_day)) +
    geom_histogram(binwidth = 1, color = "black") +
    scale_x_continuous(trans = "log2")

Stratify and Boxplot

Make boxplots stratified by a categorical variable using the geom_boxplot() geometry.

Rotate axis labels by changing the theme through element_text(). You can change the angle and justification of the text labels.

Show the data by adding data points to the boxplot with a geom_point() layer. This adds information beyond the five-number summary to your plot, but too many data points it can obfuscate your message.

# add dollars per day variable
gapminder <- gapminder %>%
    mutate(dollars_per_day = gdp/population/365)
    
# number of regions
length(levels(gapminder$region))
[1] 22
# boxplot of GDP by region in 1970
past_year <- 1970
p <- gapminder %>%
    filter(year == past_year & !is.na(gdp)) %>%
    ggplot(aes(region, dollars_per_day))
p + geom_boxplot()


# rotate names on x-axis
p + geom_boxplot() +
    theme(axis.text.x = element_text(angle = 90, hjust = 1))

Reordering

Consider ordering your factors by a meaningful value with the reorder() function, which changes the order of factor levels based on a related numeric vector. This is a way to ease comparisons.

# by default, factor order is alphabetical
fac <- factor(c("Asia", "Asia", "West", "West", "West"))
levels(fac)
[1] "Asia" "West"
# reorder factor by the category means
value <- c(10, 11, 12, 6, 4)
fac <- reorder(fac, value, FUN = mean)
levels(fac)
[1] "West" "Asia"

Looking again at the example:

# reorder by median income and color by continent
p <- gapminder %>%
    filter(year == past_year & !is.na(gdp)) %>%
    mutate(region = reorder(region, dollars_per_day, FUN = median)) %>%    # reorder
    ggplot(aes(region, dollars_per_day, fill = continent)) +    # color by continent
    geom_boxplot() +
    theme(axis.text.x = element_text(angle = 90, hjust = 1)) +
    xlab("")
p


# log2 scale y-axis
p + scale_y_continuous(trans = "log2")

For this plot, there is not a lot of data, so it can be helpful to also show the data points.

# add data points
p + scale_y_continuous(trans = "log2") + geom_point(show.legend = FALSE)

Comparing Distributions

Use intersect() to find the overlap between two vectors.

To make boxplots where grouped variables are adjacaent, color the boxplot by a factor instead of faceting by that factor. This is a way to ease comparisons.

# add dollars per day variable and define past year
gapminder <- gapminder %>%
    mutate(dollars_per_day = gdp/population/365)
past_year <- 1970

# define Western countries
west <- c("Western Europe", "Northern Europe", "Southern Europe", "Northern America", "Australia and New Zealand")

# facet by West vs devloping
gapminder %>%
    filter(year == past_year & !is.na(gdp)) %>%
    mutate(group = ifelse(region %in% west, "West", "Developing")) %>%
    ggplot(aes(dollars_per_day)) +
    geom_histogram(binwidth = 1, color = "black") +
    scale_x_continuous(trans = "log2") +
    facet_grid(. ~ group)


# facet by West/developing and year
present_year <- 2010
gapminder %>%
    filter(year %in% c(past_year, present_year) & !is.na(gdp)) %>%
    mutate(group = ifelse(region %in% west, "West", "Developing")) %>%
    ggplot(aes(dollars_per_day)) +
    geom_histogram(binwidth = 1, color = "black") +
    scale_x_continuous(trans = "log2") +
    facet_grid(year ~ group)

# define countries that have data available in both years
country_list_1 <- gapminder %>%
    filter(year == past_year & !is.na(dollars_per_day)) %>% .$country

country_list_2 <- gapminder %>%
    filter(year == present_year & !is.na(dollars_per_day)) %>% .$country

country_list <- intersect(country_list_1, country_list_2)

# make histogram including only countries with data available in both years
gapminder %>%
    filter(year %in% c(past_year, present_year) & country %in% country_list) %>%    # keep only selected countries
    mutate(group = ifelse(region %in% west, "West", "Developing")) %>%
    ggplot(aes(dollars_per_day)) +
    geom_histogram(binwidth = 1, color = "black") +
    scale_x_continuous(trans = "log2") +
    facet_grid(year ~ group)

p <- gapminder %>%
    filter(year %in% c(past_year, present_year) & country %in% country_list) %>%
    mutate(region = reorder(region, dollars_per_day, FUN = median)) %>%
    ggplot() +
    theme(axis.text.x = element_text(angle = 90, hjust = 1)) +
    xlab("") + scale_y_continuous(trans = "log2")
    
 p + geom_boxplot(aes(region, dollars_per_day, fill = continent)) +
     facet_grid(year ~ .)

It would be clearer to compare if the years were side by side. By simply filling by factor(year) in the mapping, ggplot will automatically separate them.

# arrange matching boxplots next to each other, colored by year
 p + geom_boxplot(aes(region, dollars_per_day, fill = factor(year)))

Density Plots

Change the y-axis of density plots to variable counts using ..count.. as the y argument.

The case_when() function defines a factor whose levels are defined by a variety of logical operations to group data.

Plot stacked density plots using position=“stack”.

Define a weight aesthetic mapping to change the relative weights of density plots - for example, this allows weighting of plots by population rather than number of countries.

# see the code below the previous video for variable definitions

# smooth density plots - area under each curve adds to 1
gapminder %>%
    filter(year == past_year & country %in% country_list) %>%
    mutate(group = ifelse(region %in% west, "West", "Developing")) %>% group_by(group) %>%
    summarize(n = n()) %>% knitr::kable()
group n
Developing 87
West 21

# smooth density plots - variable counts on y-axis
p <- gapminder %>%
    filter(year == past_year & country %in% country_list) %>%
    mutate(group = ifelse(region %in% west, "West", "Developing")) %>%
    ggplot(aes(dollars_per_day, y = ..count.., fill = group)) +
    scale_x_continuous(trans = "log2")
p + geom_density(alpha = 0.2) + facet_grid(year ~ .)

Include the present year, and then add smoothness with the bw arg in aes():

# smooth density plots - variable counts on y-axis
p <- gapminder %>%
    filter(year %in% c(past_year, present_year) & country %in% country_list) %>%
    mutate(group = ifelse(region %in% west, "West", "Developing")) %>%
    ggplot(aes(dollars_per_day, y = ..count.., fill = group)) +
    scale_x_continuous(trans = "log2")
p + geom_density(alpha = 0.2, bw = 0.75) + facet_grid(year ~ .)

Like with the ifelse function, case_when() can define results based on conditional expressions, but is able to have multiple options.

# add group as a factor, grouping regions
gapminder <- gapminder %>%
    mutate(group = case_when(
            .$region %in% west ~ "West",
            .$region %in% c("Eastern Asia", "South-Eastern Asia") ~ "East Asia",
            .$region %in% c("Caribbean", "Central America", "South America") ~ "Latin America",
            .$continent == "Africa" & .$region != "Northern Africa" ~ "Sub-Saharan Africa",
            TRUE ~ "Others"))

# reorder factor levels
gapminder <- gapminder %>%
    mutate(group = factor(group, levels = c("Others", "Latin America", "East Asia", "Sub-Saharan Africa", "West")))

The new groups can be separated in the mapping.

# smooth density plots - variable counts on y-axis
p <- gapminder %>%
    filter(year %in% c(past_year, present_year) & country %in% country_list) %>%
    ggplot(aes(dollars_per_day, y = ..count.., fill = group)) +
    scale_x_continuous(trans = "log2")
p + geom_density(alpha = 0.2, bw = 0.75) + facet_grid(year ~ .)

A stacked density plot may be a better way to display these groups, as it will be less cluttered.

# note you must redefine p with the new gapminder object first
p <- gapminder %>%
  filter(year %in% c(past_year, present_year) & country %in% country_list) %>%
    ggplot(aes(dollars_per_day, fill = group)) +
    scale_x_continuous(trans = "log2")

# stacked density plot
p + geom_density(alpha = 0.2, bw = 0.75, position = "stack") +
    facet_grid(year ~ .)

A weighting can be defined as a new field, and then the weight argument in the mapping will make the adjustments for the plot.

# weighted stacked density plot
gapminder %>%
    filter(year %in% c(past_year, present_year) & country %in% country_list) %>%
    group_by(year) %>%
    mutate(weight = population/sum(population*2)) %>%
    ungroup() %>%
    ggplot(aes(dollars_per_day, fill = group, weight = weight)) +
    scale_x_continuous(trans = "log2") +
    geom_density(alpha = 0.2, bw = 0.75, position = "stack") + facet_grid(year ~ .)

Logistic Transformation

The logistic or logit transformation is defined as 𝑓(𝑝)=log𝑝1−𝑝, or the log of odds. This scale is useful for highlighting differences near 0 or near 1 and converts fold changes into constant increases.

# define gapminder
library(tidyverse)
library(dslabs)
data(gapminder)

# add additional cases
gapminder <- gapminder %>%
    mutate(group = case_when(
        .$region %in% west ~ "The West",
        .$region %in% "Northern Africa" ~ "Northern Africa",
        .$region %in% c("Eastern Asia", "South-Eastern Asia") ~ "East Asia",
        .$region == "Southern Asia" ~ "Southern Asia",
        .$region %in% c("Central America", "South America", "Caribbean") ~ "Latin America",
        .$continent == "Africa" & .$region != "Northern Africa" ~ "Sub-Saharan Africa",
        .$region %in% c("Melanesia", "Micronesia", "Polynesia") ~ "Pacific Islands"))

# define a data frame with group average income and average infant survival rate
surv_income <- gapminder %>%
    filter(year %in% present_year & !is.na(gdp) & !is.na(infant_mortality) & !is.na(group)) %>%
    group_by(group) %>%
    summarize(income = sum(gdp)/sum(population)/365,
                        infant_survival_rate = 1 - sum(infant_mortality/1000*population)/sum(population))
surv_income %>% arrange(income)

# plot infant survival versus income, with transformed axes
surv_income %>% ggplot(aes(income, infant_survival_rate, label = group, color = group)) +
    scale_x_continuous(trans = "log2", limit = c(0.25, 150)) +
    scale_y_continuous(trans = "logit", limit = c(0.875, .9981),
                                       breaks = c(.85, .90, .95, .99, .995, .998)) +
    geom_label(size = 3, show.legend = FALSE)

The ecological fallacy is assuming that conclusions made from the average of a group apply to all members of that group.

Removing the grouping, all the data is shown again.

# plot infant survival versus income, with transformed axes
gapminder %>% 
  filter(year %in% present_year & !is.na(gdp) & !is.na(infant_mortality) & !is.na(group)) %>%
  mutate(dollars_per_day = gdp/population/365) %>%
    ggplot(aes(dollars_per_day, 1-infant_mortality/1000, color = group)) +
  scale_x_continuous(trans = "log2", limit = c(0.25, 150)) +
  scale_y_continuous(trans = "logit", limit = c(0.875, .9981),
                     breaks = c(.85, .90, .95, .99, .995, .998)) +
  geom_point() 

LS0tCnRpdGxlOiAiR2FwbWluZGVyIgpvdXRwdXQ6IGh0bWxfbm90ZWJvb2sKLS0tCgojIyBMb2FkIGFuZCBFeHBsb3JlIHRoZSBEYXRhCgpgYGB7cn0KIyBsb2FkIGFuZCBpbnNwZWN0IGdhcG1pbmRlciBkYXRhCmxpYnJhcnkoZHNsYWJzKQpkYXRhKGdhcG1pbmRlcikKaGVhZChnYXBtaW5kZXIpCmBgYAoKIyMjIExpZmUgRXhwZWN0YW5jeSBhbmQgRmVydGlsaXR5IFJhdGVzCgpgYGB7cn0KIyBjb21wYXJlIGluZmFudCBtb3J0YWxpdHkgaW4gU3JpIExhbmthIGFuZCBUdXJrZXkKZ2FwbWluZGVyICU+JQogICAgZmlsdGVyKHllYXIgPT0gMjAxNSAmIGNvdW50cnkgJWluJSBjKCJTcmkgTGFua2EiLCAiVHVya2V5IikpICU+JQogICAgc2VsZWN0KGNvdW50cnksIGluZmFudF9tb3J0YWxpdHkpCmBgYAoKYGBge3J9CiMgYmFzaWMgc2NhdHRlcnBsb3Qgb2YgbGlmZSBleHBlY3RhbmN5IHZlcnN1cyBmZXJ0aWxpdHkKZHNfdGhlbWVfc2V0KCkgICAgIyBzZXQgcGxvdCB0aGVtZQpmaWx0ZXIoZ2FwbWluZGVyLCB5ZWFyID09IDE5NjIpICU+JQogICAgZ2dwbG90KGFlcyhmZXJ0aWxpdHksIGxpZmVfZXhwZWN0YW5jeSkpICsKICAgIGdlb21fcG9pbnQoKQogICAgCiMgYWRkIGNvbG9yIGFzIGNvbnRpbmVudApmaWx0ZXIoZ2FwbWluZGVyLCB5ZWFyID09IDE5NjIpICU+JQogICAgZ2dwbG90KGFlcyhmZXJ0aWxpdHksIGxpZmVfZXhwZWN0YW5jeSwgY29sb3IgPSBjb250aW5lbnQpKSArCiAgICBnZW9tX3BvaW50KCkKYGBgCgojIyMgRmFjZXRpbmcKCkZhY2V0aW5nIG1ha2VzIG11bHRpcGxlIHNpZGUtYnktc2lkZSBwbG90cyBzdHJhdGlmaWVkIGJ5IHNvbWUgdmFyaWFibGUuIFRoaXMgaXMgYSB3YXkgdG8gZWFzZSBjb21wYXJpc29ucy4KClRoZSBmYWNldF9ncmlkKCkgZnVuY3Rpb24gYWxsb3dzIGZhY2V0aW5nIGJ5IHVwIHRvIHR3byB2YXJpYWJsZXMsIHdpdGggcm93cyBmYWNldGVkIGJ5IG9uZSB2YXJpYWJsZSBhbmQgY29sdW1ucyBmYWNldGVkIGJ5IHRoZSBvdGhlciB2YXJpYWJsZS4gVG8gZmFjZXQgYnkgb25seSBvbmUgdmFyaWFibGUsIHVzZSB0aGUgZG90IG9wZXJhdG9yIGFzIHRoZSBvdGhlciB2YXJpYWJsZS4KClRoZSBmYWNldF93cmFwKCkgZnVuY3Rpb24gZmFjZXRzIGJ5IG9uZSB2YXJpYWJsZSBhbmQgYXV0b21hdGljYWxseSB3cmFwcyB0aGUgc2VyaWVzIG9mIHBsb3RzIHNvIHRoZXkgaGF2ZSByZWFkYWJsZSBkaW1lbnNpb25zLgoKRmFjZXRpbmcga2VlcHMgdGhlIGF4ZXMgZml4ZWQgYWNyb3NzIGFsbCBwbG90cywgZWFzaW5nIGNvbXBhcmlzb25zIGJldHdlZW4gcGxvdHMuCgpgYGB7cn0KIyBmYWNldCBieSBjb250aW5lbnQgYW5kIHllYXIKZmlsdGVyKGdhcG1pbmRlciwgeWVhciAlaW4lIGMoMTk2MiwgMjAxMikpICU+JQogICAgZ2dwbG90KGFlcyhmZXJ0aWxpdHksIGxpZmVfZXhwZWN0YW5jeSwgY29sID0gY29udGluZW50KSkgKwogICAgZ2VvbV9wb2ludCgpICsKICAgIGZhY2V0X2dyaWQoY29udGluZW50IH4geWVhcikKCiMgZmFjZXQgYnkgeWVhciBvbmx5CmZpbHRlcihnYXBtaW5kZXIsIHllYXIgJWluJSBjKDE5NjIsIDIwMTIpKSAlPiUKICAgIGdncGxvdChhZXMoZmVydGlsaXR5LCBsaWZlX2V4cGVjdGFuY3ksIGNvbCA9IGNvbnRpbmVudCkpICsKICAgIGdlb21fcG9pbnQoKSArCiAgICBmYWNldF9ncmlkKC4gfiB5ZWFyKQoKIyBmYWNldCBieSB5ZWFyLCBwbG90cyB3cmFwcGVkIG9udG8gbXVsdGlwbGUgcm93cwp5ZWFycyA8LSBjKDE5NjIsIDE5ODAsIDE5OTAsIDIwMDAsIDIwMTIpCmNvbnRpbmVudHMgPC0gYygiRXVyb3BlIiwgIkFzaWEiKQpnYXBtaW5kZXIgJT4lCiAgICBmaWx0ZXIoeWVhciAlaW4lIHllYXJzICYgY29udGluZW50ICVpbiUgY29udGluZW50cykgJT4lCiAgICBnZ3Bsb3QoYWVzKGZlcnRpbGl0eSwgbGlmZV9leHBlY3RhbmN5LCBjb2wgPSBjb250aW5lbnQpKSArCiAgICBnZW9tX3BvaW50KCkgKwogICAgZmFjZXRfd3JhcCh+eWVhcikKYGBgCgojIyMgVGltZSBTZXJpZXMgUGxvdHMKClRoZSBnZW9tX2xpbmUoKSBnZW9tZXRyeSBjb25uZWN0cyBhZGphY2VudCBkYXRhIHBvaW50cyB0byBmb3JtIGEgY29udGludW91cyBsaW5lLiBBIGxpbmUgcGxvdCBpcyBhcHByb3ByaWF0ZSB3aGVuIHBvaW50cyBhcmUgcmVndWxhcmx5IHNwYWNlZCwgZGVuc2VseSBwYWNrZWQgYW5kIGZyb20gYSBzaW5nbGUgZGF0YSBzZXJpZXMuCgogWW91IGNhbiBwbG90IG11bHRpcGxlIGxpbmVzIG9uIHRoZSBzYW1lIGdyYXBoLiBSZW1lbWJlciB0byBncm91cCBvciBjb2xvciBieSBhIHZhcmlhYmxlIHNvIHRoYXQgdGhlIGxpbmVzIGFyZSBwbG90dGVkIGluZGVwZW5kZW50bHkuCiAKTGFiZWxpbmcgaXMgdXN1YWxseSBwcmVmZXJyZWQgb3ZlciBsZWdlbmRzLiBIb3dldmVyLCBsZWdlbmRzIGFyZSBlYXNpZXIgdG8gbWFrZSBhbmQgYXBwZWFyIGJ5IGRlZmF1bHQuIEFkZCBhIGxhYmVsIHdpdGggZ2VvbV90ZXh0KCksIHNwZWNpZnlpbmcgdGhlIGNvb3JkaW5hdGVzIHdoZXJlIHRoZSBsYWJlbCBzaG91bGQgYXBwZWFyIG9uIHRoZSBncmFwaC4KCiMjIyMgU2luZ2xlIFRpbWUgU2VyaWVzCgpgYGB7cn0KIyBzY2F0dGVycGxvdCBvZiBVUyBmZXJ0aWxpdHkgYnkgeWVhcgpnYXBtaW5kZXIgJT4lCiAgICBmaWx0ZXIoY291bnRyeSA9PSAiVW5pdGVkIFN0YXRlcyIpICU+JQogICAgZ2dwbG90KGFlcyh5ZWFyLCBmZXJ0aWxpdHkpKSArCiAgICBnZW9tX3BvaW50KCkKCiMgbGluZSBwbG90IG9mIFVTIGZlcnRpbGl0eSBieSB5ZWFyCmdhcG1pbmRlciAlPiUKICAgIGZpbHRlcihjb3VudHJ5ID09ICJVbml0ZWQgU3RhdGVzIikgJT4lCiAgICBnZ3Bsb3QoYWVzKHllYXIsIGZlcnRpbGl0eSkpICsKICAgIGdlb21fbGluZSgpCmBgYAojIyMjIE11bHRpcGxlIFRpbWUgU2VyaWVzCgpgYGB7cn0KIyBsaW5lIHBsb3QgZmVydGlsaXR5IHRpbWUgc2VyaWVzIGZvciB0d28gY291bnRyaWVzLSBvbmx5IG9uZSBsaW5lIChpbmNvcnJlY3QpCmNvdW50cmllcyA8LSBjKCJTb3V0aCBLb3JlYSIsICJHZXJtYW55IikKZ2FwbWluZGVyICU+JSBmaWx0ZXIoY291bnRyeSAlaW4lIGNvdW50cmllcykgJT4lCiAgICBnZ3Bsb3QoYWVzKHllYXIsIGZlcnRpbGl0eSkpICsKICAgIGdlb21fbGluZSgpCiAgICAKIyBsaW5lIHBsb3QgZmVydGlsaXR5IHRpbWUgc2VyaWVzIGZvciB0d28gY291bnRyaWVzIC0gb25lIGxpbmUgcGVyIGNvdW50cnkKZ2FwbWluZGVyICU+JSBmaWx0ZXIoY291bnRyeSAlaW4lIGNvdW50cmllcykgJT4lCiAgICBnZ3Bsb3QoYWVzKHllYXIsIGZlcnRpbGl0eSwgZ3JvdXAgPSBjb3VudHJ5KSkgKwogICAgZ2VvbV9saW5lKCkKCiMgZmVydGlsaXR5IHRpbWUgc2VyaWVzIGZvciB0d28gY291bnRyaWVzIC0gbGluZXMgY29sb3JlZCBieSBjb3VudHJ5CmdhcG1pbmRlciAlPiUgZmlsdGVyKGNvdW50cnkgJWluJSBjb3VudHJpZXMpICU+JQogICAgZ2dwbG90KGFlcyh5ZWFyLCBmZXJ0aWxpdHksIGNvbCA9IGNvdW50cnkpKSArCiAgICBnZW9tX2xpbmUoKQpgYGAKIyMjIyBBZGRpbmcgVGV4dCBMYWJlbHMgdG8gYSBQbG90CgpgYGB7cn0KIyBsaWZlIGV4cGVjdGFuY3kgdGltZSBzZXJpZXMgLSBsaW5lcyBjb2xvcmVkIGJ5IGNvdW50cnkgYW5kIGxhYmVsZWQsIG5vIGxlZ2VuZApsYWJlbHMgPC0gZGF0YS5mcmFtZShjb3VudHJ5ID0gY291bnRyaWVzLCB4ID0gYygxOTc1LCAxOTY1KSwgeSA9IGMoNjAsIDcyKSkKZ2FwbWluZGVyICU+JSBmaWx0ZXIoY291bnRyeSAlaW4lIGNvdW50cmllcykgJT4lCiAgICBnZ3Bsb3QoYWVzKHllYXIsIGxpZmVfZXhwZWN0YW5jeSwgY29sID0gY291bnRyeSkpICsKICAgIGdlb21fbGluZSgpICsKICAgIGdlb21fdGV4dChkYXRhID0gbGFiZWxzLCBhZXMoeCwgeSwgbGFiZWwgPSBjb3VudHJ5KSwgc2l6ZSA9IDUpICsKICAgIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJub25lIikKYGBgCgojIyMgVHJhbnNmb3JtYXRpb25zCgpXZSB1c2UgR0RQIGRhdGEgdG8gY29tcHV0ZSBpbmNvbWUgaW4gVVMgZG9sbGFycyBwZXIgZGF5LCBhZGp1c3RlZCBmb3IgaW5mbGF0aW9uLgoKTG9nIHRyYW5zZm9ybWF0aW9ucyBjb252ZXJ0IG11bHRpcGxpY2F0aXZlIGNoYW5nZXMgaW50byBhZGRpdGl2ZSBjaGFuZ2VzLgpDb21tb24gdHJhbnNmb3JtYXRpb25zIGFyZSB0aGUgbG9nIGJhc2UgMiB0cmFuc2Zvcm1hdGlvbiBhbmQgdGhlIGxvZyBiYXNlIDEwIHRyYW5zZm9ybWF0aW9uLiBUaGUgY2hvaWNlIG9mIGJhc2UgZGVwZW5kcyBvbiB0aGUgcmFuZ2Ugb2YgdGhlIGRhdGEuIFRoZSBuYXR1cmFsIGxvZyBpcyBub3QgcmVjb21tZW5kZWQgZm9yIHZpc3VhbGl6YXRpb24gYmVjYXVzZSBpdCBpcyBkaWZmaWN1bHQgdG8gaW50ZXJwcmV0LgoKVGhlIG1vZGUgb2YgYSBkaXN0cmlidXRpb24gaXMgdGhlIHZhbHVlIHdpdGggdGhlIGhpZ2hlc3QgZnJlcXVlbmN5LiBUaGUgbW9kZSBvZiBhIG5vcm1hbCBkaXN0cmlidXRpb24gaXMgdGhlIGF2ZXJhZ2UuIEEgZGlzdHJpYnV0aW9uIGNhbiBoYXZlIG11bHRpcGxlIGxvY2FsIG1vZGVzLgoKVGhlcmUgYXJlIHR3byB3YXlzIHRvIHVzZSBsb2cgdHJhbnNmb3JtYXRpb25zIGluIHBsb3RzOiB0cmFuc2Zvcm0gdGhlIGRhdGEgYmVmb3JlIHBsb3R0aW5nIG9yIHRyYW5zZm9ybSB0aGUgYXhlcyBvZiB0aGUgcGxvdC4gTG9nIHNjYWxlcyBoYXZlIHRoZSBhZHZhbnRhZ2Ugb2Ygc2hvd2luZyB0aGUgb3JpZ2luYWwgdmFsdWVzIGFzIGF4aXMgbGFiZWxzLCB3aGlsZSBsb2cgdHJhbnNmb3JtZWQgdmFsdWVzIGVhc2UgaW50ZXJwcmV0YXRpb24gb2YgaW50ZXJtZWRpYXRlIHZhbHVlcyBiZXR3ZWVuIGxhYmVscy4KClNjYWxlIHRoZSB4LWF4aXMgdXNpbmcgc2NhbGVfeF9jb250aW51b3VzKCkgb3Igc2NhbGVfeF9sb2cxMCgpIGxheWVycyBpbiBnZ3Bsb3QyLiBTaW1pbGFyIGZ1bmN0aW9ucyBleGlzdCBmb3IgdGhlIHktYXhpcy4KCkluIDE5NzAsIGluY29tZSBkaXN0cmlidXRpb24gaXMgYmltb2RhbCwgY29uc2lzdGVudCB3aXRoIHRoZSBkaWNob3RvbW91cyBXZXN0ZXJuIHZlcnN1cyBkZXZlbG9waW5nIHdvcmxkdmlldy4KCmBgYHtyfQojIGFkZCBkb2xsYXJzIHBlciBkYXkgdmFyaWFibGUKZ2FwbWluZGVyIDwtIGdhcG1pbmRlciAlPiUKICAgIG11dGF0ZShkb2xsYXJzX3Blcl9kYXkgPSBnZHAvcG9wdWxhdGlvbi8zNjUpCgojIGhpc3RvZ3JhbSBvZiBkb2xsYXJzIHBlciBkYXkKcGFzdF95ZWFyIDwtIDE5NzAKZ2FwbWluZGVyICU+JQogICAgZmlsdGVyKHllYXIgPT0gcGFzdF95ZWFyICYgIWlzLm5hKGdkcCkpICU+JQogICAgZ2dwbG90KGFlcyhkb2xsYXJzX3Blcl9kYXkpKSArCiAgICBnZW9tX2hpc3RvZ3JhbShiaW53aWR0aCA9IDEsIGNvbG9yID0gImJsYWNrIikKCiMgcmVwZWF0IGhpc3RvZ3JhbSB3aXRoIGxvZzIgc2NhbGVkIGRhdGEKZ2FwbWluZGVyICU+JQogICAgZmlsdGVyKHllYXIgPT0gcGFzdF95ZWFyICYgIWlzLm5hKGdkcCkpICU+JQogICAgZ2dwbG90KGFlcyhsb2cyKGRvbGxhcnNfcGVyX2RheSkpKSArCiAgICBnZW9tX2hpc3RvZ3JhbShiaW53aWR0aCA9IDEsIGNvbG9yID0gImJsYWNrIikKICAgIAojIHJlcGVhdCBoaXN0b2dyYW0gd2l0aCBsb2cyIHNjYWxlZCB4LWF4aXMKZ2FwbWluZGVyICU+JQogICAgZmlsdGVyKHllYXIgPT0gcGFzdF95ZWFyICYgIWlzLm5hKGdkcCkpICU+JQogICAgZ2dwbG90KGFlcyhkb2xsYXJzX3Blcl9kYXkpKSArCiAgICBnZW9tX2hpc3RvZ3JhbShiaW53aWR0aCA9IDEsIGNvbG9yID0gImJsYWNrIikgKwogICAgc2NhbGVfeF9jb250aW51b3VzKHRyYW5zID0gImxvZzIiKQpgYGAKCiMjIyBTdHJhdGlmeSBhbmQgQm94cGxvdAoKTWFrZSBib3hwbG90cyBzdHJhdGlmaWVkIGJ5IGEgY2F0ZWdvcmljYWwgdmFyaWFibGUgdXNpbmcgdGhlIGBnZW9tX2JveHBsb3QoKWAgZ2VvbWV0cnkuCgpSb3RhdGUgYXhpcyBsYWJlbHMgYnkgY2hhbmdpbmcgdGhlIHRoZW1lIHRocm91Z2ggYGVsZW1lbnRfdGV4dCgpYC4gWW91IGNhbiBjaGFuZ2UgdGhlIGFuZ2xlIGFuZCBqdXN0aWZpY2F0aW9uIG9mIHRoZSB0ZXh0IGxhYmVscy4KClNob3cgdGhlIGRhdGEgYnkgYWRkaW5nIGRhdGEgcG9pbnRzIHRvIHRoZSBib3hwbG90IHdpdGggYSBgZ2VvbV9wb2ludCgpYCBsYXllci4gVGhpcyBhZGRzIGluZm9ybWF0aW9uIGJleW9uZCB0aGUgZml2ZS1udW1iZXIgc3VtbWFyeSB0byB5b3VyIHBsb3QsIGJ1dCB0b28gbWFueSBkYXRhIHBvaW50cyBpdCBjYW4gb2JmdXNjYXRlIHlvdXIgbWVzc2FnZS4KCmBgYHtyfQojIGFkZCBkb2xsYXJzIHBlciBkYXkgdmFyaWFibGUKZ2FwbWluZGVyIDwtIGdhcG1pbmRlciAlPiUKICAgIG11dGF0ZShkb2xsYXJzX3Blcl9kYXkgPSBnZHAvcG9wdWxhdGlvbi8zNjUpCiAgICAKIyBudW1iZXIgb2YgcmVnaW9ucwpsZW5ndGgobGV2ZWxzKGdhcG1pbmRlciRyZWdpb24pKQoKIyBib3hwbG90IG9mIEdEUCBieSByZWdpb24gaW4gMTk3MApwYXN0X3llYXIgPC0gMTk3MApwIDwtIGdhcG1pbmRlciAlPiUKICAgIGZpbHRlcih5ZWFyID09IHBhc3RfeWVhciAmICFpcy5uYShnZHApKSAlPiUKICAgIGdncGxvdChhZXMocmVnaW9uLCBkb2xsYXJzX3Blcl9kYXkpKQpwICsgZ2VvbV9ib3hwbG90KCkKCiMgcm90YXRlIG5hbWVzIG9uIHgtYXhpcwpwICsgZ2VvbV9ib3hwbG90KCkgKwogICAgdGhlbWUoYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoYW5nbGUgPSA5MCwgaGp1c3QgPSAxKSkKYGBgCgojIyMjIFJlb3JkZXJpbmcKCkNvbnNpZGVyIG9yZGVyaW5nIHlvdXIgZmFjdG9ycyBieSBhIG1lYW5pbmdmdWwgdmFsdWUgd2l0aCB0aGUgYHJlb3JkZXIoKWAgZnVuY3Rpb24sIHdoaWNoIGNoYW5nZXMgdGhlIG9yZGVyIG9mIGZhY3RvciBsZXZlbHMgYmFzZWQgb24gYSByZWxhdGVkIG51bWVyaWMgdmVjdG9yLiBUaGlzIGlzIGEgd2F5IHRvIGVhc2UgY29tcGFyaXNvbnMuCgpgYGB7cn0KIyBieSBkZWZhdWx0LCBmYWN0b3Igb3JkZXIgaXMgYWxwaGFiZXRpY2FsCmZhYyA8LSBmYWN0b3IoYygiQXNpYSIsICJBc2lhIiwgIldlc3QiLCAiV2VzdCIsICJXZXN0IikpCmxldmVscyhmYWMpCgojIHJlb3JkZXIgZmFjdG9yIGJ5IHRoZSBjYXRlZ29yeSBtZWFucwp2YWx1ZSA8LSBjKDEwLCAxMSwgMTIsIDYsIDQpCmZhYyA8LSByZW9yZGVyKGZhYywgdmFsdWUsIEZVTiA9IG1lYW4pCmxldmVscyhmYWMpCmBgYAoKTG9va2luZyBhZ2FpbiBhdCB0aGUgZXhhbXBsZToKCmBgYHtyfQojIHJlb3JkZXIgYnkgbWVkaWFuIGluY29tZSBhbmQgY29sb3IgYnkgY29udGluZW50CnAgPC0gZ2FwbWluZGVyICU+JQogICAgZmlsdGVyKHllYXIgPT0gcGFzdF95ZWFyICYgIWlzLm5hKGdkcCkpICU+JQogICAgbXV0YXRlKHJlZ2lvbiA9IHJlb3JkZXIocmVnaW9uLCBkb2xsYXJzX3Blcl9kYXksIEZVTiA9IG1lZGlhbikpICU+JSAgICAjIHJlb3JkZXIKICAgIGdncGxvdChhZXMocmVnaW9uLCBkb2xsYXJzX3Blcl9kYXksIGZpbGwgPSBjb250aW5lbnQpKSArICAgICMgY29sb3IgYnkgY29udGluZW50CiAgICBnZW9tX2JveHBsb3QoKSArCiAgICB0aGVtZShheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChhbmdsZSA9IDkwLCBoanVzdCA9IDEpKSArCiAgICB4bGFiKCIiKQpwCgojIGxvZzIgc2NhbGUgeS1heGlzCnAgKyBzY2FsZV95X2NvbnRpbnVvdXModHJhbnMgPSAibG9nMiIpCmBgYAoKRm9yIHRoaXMgcGxvdCwgdGhlcmUgaXMgbm90IGEgbG90IG9mIGRhdGEsIHNvIGl0IGNhbiBiZSBoZWxwZnVsIHRvIGFsc28gc2hvdyB0aGUgZGF0YSBwb2ludHMuCgpgYGB7cn0KIyBhZGQgZGF0YSBwb2ludHMKcCArIHNjYWxlX3lfY29udGludW91cyh0cmFucyA9ICJsb2cyIikgKyBnZW9tX3BvaW50KHNob3cubGVnZW5kID0gRkFMU0UpCmBgYAoKIyMjIENvbXBhcmluZyBEaXN0cmlidXRpb25zCgpVc2UgaW50ZXJzZWN0KCkgdG8gZmluZCB0aGUgb3ZlcmxhcCBiZXR3ZWVuIHR3byB2ZWN0b3JzLgoKVG8gbWFrZSBib3hwbG90cyB3aGVyZSBncm91cGVkIHZhcmlhYmxlcyBhcmUgYWRqYWNhZW50LCBjb2xvciB0aGUgYm94cGxvdCBieSBhIGZhY3RvciBpbnN0ZWFkIG9mIGZhY2V0aW5nIGJ5IHRoYXQgZmFjdG9yLiBUaGlzIGlzIGEgd2F5IHRvIGVhc2UgY29tcGFyaXNvbnMuCgpgYGB7cn0KIyBhZGQgZG9sbGFycyBwZXIgZGF5IHZhcmlhYmxlIGFuZCBkZWZpbmUgcGFzdCB5ZWFyCmdhcG1pbmRlciA8LSBnYXBtaW5kZXIgJT4lCiAgICBtdXRhdGUoZG9sbGFyc19wZXJfZGF5ID0gZ2RwL3BvcHVsYXRpb24vMzY1KQpwYXN0X3llYXIgPC0gMTk3MAoKIyBkZWZpbmUgV2VzdGVybiBjb3VudHJpZXMKd2VzdCA8LSBjKCJXZXN0ZXJuIEV1cm9wZSIsICJOb3J0aGVybiBFdXJvcGUiLCAiU291dGhlcm4gRXVyb3BlIiwgIk5vcnRoZXJuIEFtZXJpY2EiLCAiQXVzdHJhbGlhIGFuZCBOZXcgWmVhbGFuZCIpCgojIGZhY2V0IGJ5IFdlc3QgdnMgZGV2bG9waW5nCmdhcG1pbmRlciAlPiUKICAgIGZpbHRlcih5ZWFyID09IHBhc3RfeWVhciAmICFpcy5uYShnZHApKSAlPiUKICAgIG11dGF0ZShncm91cCA9IGlmZWxzZShyZWdpb24gJWluJSB3ZXN0LCAiV2VzdCIsICJEZXZlbG9waW5nIikpICU+JQogICAgZ2dwbG90KGFlcyhkb2xsYXJzX3Blcl9kYXkpKSArCiAgICBnZW9tX2hpc3RvZ3JhbShiaW53aWR0aCA9IDEsIGNvbG9yID0gImJsYWNrIikgKwogICAgc2NhbGVfeF9jb250aW51b3VzKHRyYW5zID0gImxvZzIiKSArCiAgICBmYWNldF9ncmlkKC4gfiBncm91cCkKCiMgZmFjZXQgYnkgV2VzdC9kZXZlbG9waW5nIGFuZCB5ZWFyCnByZXNlbnRfeWVhciA8LSAyMDEwCmdhcG1pbmRlciAlPiUKICAgIGZpbHRlcih5ZWFyICVpbiUgYyhwYXN0X3llYXIsIHByZXNlbnRfeWVhcikgJiAhaXMubmEoZ2RwKSkgJT4lCiAgICBtdXRhdGUoZ3JvdXAgPSBpZmVsc2UocmVnaW9uICVpbiUgd2VzdCwgIldlc3QiLCAiRGV2ZWxvcGluZyIpKSAlPiUKICAgIGdncGxvdChhZXMoZG9sbGFyc19wZXJfZGF5KSkgKwogICAgZ2VvbV9oaXN0b2dyYW0oYmlud2lkdGggPSAxLCBjb2xvciA9ICJibGFjayIpICsKICAgIHNjYWxlX3hfY29udGludW91cyh0cmFucyA9ICJsb2cyIikgKwogICAgZmFjZXRfZ3JpZCh5ZWFyIH4gZ3JvdXApCmBgYAoKCmBgYHtyfQojIGRlZmluZSBjb3VudHJpZXMgdGhhdCBoYXZlIGRhdGEgYXZhaWxhYmxlIGluIGJvdGggeWVhcnMKY291bnRyeV9saXN0XzEgPC0gZ2FwbWluZGVyICU+JQogICAgZmlsdGVyKHllYXIgPT0gcGFzdF95ZWFyICYgIWlzLm5hKGRvbGxhcnNfcGVyX2RheSkpICU+JSAuJGNvdW50cnkKCmNvdW50cnlfbGlzdF8yIDwtIGdhcG1pbmRlciAlPiUKICAgIGZpbHRlcih5ZWFyID09IHByZXNlbnRfeWVhciAmICFpcy5uYShkb2xsYXJzX3Blcl9kYXkpKSAlPiUgLiRjb3VudHJ5Cgpjb3VudHJ5X2xpc3QgPC0gaW50ZXJzZWN0KGNvdW50cnlfbGlzdF8xLCBjb3VudHJ5X2xpc3RfMikKCiMgbWFrZSBoaXN0b2dyYW0gaW5jbHVkaW5nIG9ubHkgY291bnRyaWVzIHdpdGggZGF0YSBhdmFpbGFibGUgaW4gYm90aCB5ZWFycwpnYXBtaW5kZXIgJT4lCiAgICBmaWx0ZXIoeWVhciAlaW4lIGMocGFzdF95ZWFyLCBwcmVzZW50X3llYXIpICYgY291bnRyeSAlaW4lIGNvdW50cnlfbGlzdCkgJT4lICAgICMga2VlcCBvbmx5IHNlbGVjdGVkIGNvdW50cmllcwogICAgbXV0YXRlKGdyb3VwID0gaWZlbHNlKHJlZ2lvbiAlaW4lIHdlc3QsICJXZXN0IiwgIkRldmVsb3BpbmciKSkgJT4lCiAgICBnZ3Bsb3QoYWVzKGRvbGxhcnNfcGVyX2RheSkpICsKICAgIGdlb21faGlzdG9ncmFtKGJpbndpZHRoID0gMSwgY29sb3IgPSAiYmxhY2siKSArCiAgICBzY2FsZV94X2NvbnRpbnVvdXModHJhbnMgPSAibG9nMiIpICsKICAgIGZhY2V0X2dyaWQoeWVhciB+IGdyb3VwKQpgYGAKCmBgYHtyfQpwIDwtIGdhcG1pbmRlciAlPiUKICAgIGZpbHRlcih5ZWFyICVpbiUgYyhwYXN0X3llYXIsIHByZXNlbnRfeWVhcikgJiBjb3VudHJ5ICVpbiUgY291bnRyeV9saXN0KSAlPiUKICAgIG11dGF0ZShyZWdpb24gPSByZW9yZGVyKHJlZ2lvbiwgZG9sbGFyc19wZXJfZGF5LCBGVU4gPSBtZWRpYW4pKSAlPiUKICAgIGdncGxvdCgpICsKICAgIHRoZW1lKGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KGFuZ2xlID0gOTAsIGhqdXN0ID0gMSkpICsKICAgIHhsYWIoIiIpICsgc2NhbGVfeV9jb250aW51b3VzKHRyYW5zID0gImxvZzIiKQogICAgCiBwICsgZ2VvbV9ib3hwbG90KGFlcyhyZWdpb24sIGRvbGxhcnNfcGVyX2RheSwgZmlsbCA9IGNvbnRpbmVudCkpICsKICAgICBmYWNldF9ncmlkKHllYXIgfiAuKQpgYGAKCkl0IHdvdWxkIGJlIGNsZWFyZXIgdG8gY29tcGFyZSBpZiB0aGUgeWVhcnMgd2VyZSBzaWRlIGJ5IHNpZGUuIEJ5IHNpbXBseSBmaWxsaW5nIGJ5IGBmYWN0b3IoeWVhcilgIGluIHRoZSBtYXBwaW5nLCBgZ2dwbG90YCB3aWxsIGF1dG9tYXRpY2FsbHkgc2VwYXJhdGUgdGhlbS4KCmBgYHtyfQojIGFycmFuZ2UgbWF0Y2hpbmcgYm94cGxvdHMgbmV4dCB0byBlYWNoIG90aGVyLCBjb2xvcmVkIGJ5IHllYXIKIHAgKyBnZW9tX2JveHBsb3QoYWVzKHJlZ2lvbiwgZG9sbGFyc19wZXJfZGF5LCBmaWxsID0gZmFjdG9yKHllYXIpKSkKYGBgCgojIyMgRGVuc2l0eSBQbG90cwoKICBDaGFuZ2UgdGhlIHktYXhpcyBvZiBkZW5zaXR5IHBsb3RzIHRvIHZhcmlhYmxlIGNvdW50cyB1c2luZyAuLmNvdW50Li4gYXMgdGhlIHkgYXJndW1lbnQuCgogIFRoZSBjYXNlX3doZW4oKSBmdW5jdGlvbiBkZWZpbmVzIGEgZmFjdG9yIHdob3NlIGxldmVscyBhcmUgZGVmaW5lZCBieSBhIHZhcmlldHkgb2YgbG9naWNhbCBvcGVyYXRpb25zIHRvIGdyb3VwIGRhdGEuCgogIFBsb3Qgc3RhY2tlZCBkZW5zaXR5IHBsb3RzIHVzaW5nIHBvc2l0aW9uPSJzdGFjayIuCgogIERlZmluZSBhIHdlaWdodCBhZXN0aGV0aWMgbWFwcGluZyB0byBjaGFuZ2UgdGhlIHJlbGF0aXZlIHdlaWdodHMgb2YgZGVuc2l0eSBwbG90cyAtIGZvciBleGFtcGxlLCB0aGlzIGFsbG93cyB3ZWlnaHRpbmcgb2YgcGxvdHMgYnkgcG9wdWxhdGlvbiByYXRoZXIgdGhhbiBudW1iZXIgb2YgY291bnRyaWVzLgoKYGBge3J9CiMgc2VlIHRoZSBjb2RlIGJlbG93IHRoZSBwcmV2aW91cyB2aWRlbyBmb3IgdmFyaWFibGUgZGVmaW5pdGlvbnMKCiMgc21vb3RoIGRlbnNpdHkgcGxvdHMgLSBhcmVhIHVuZGVyIGVhY2ggY3VydmUgYWRkcyB0byAxCmdhcG1pbmRlciAlPiUKICAgIGZpbHRlcih5ZWFyID09IHBhc3RfeWVhciAmIGNvdW50cnkgJWluJSBjb3VudHJ5X2xpc3QpICU+JQogICAgbXV0YXRlKGdyb3VwID0gaWZlbHNlKHJlZ2lvbiAlaW4lIHdlc3QsICJXZXN0IiwgIkRldmVsb3BpbmciKSkgJT4lIGdyb3VwX2J5KGdyb3VwKSAlPiUKICAgIHN1bW1hcml6ZShuID0gbigpKSAlPiUga25pdHI6OmthYmxlKCkKCiMgc21vb3RoIGRlbnNpdHkgcGxvdHMgLSB2YXJpYWJsZSBjb3VudHMgb24geS1heGlzCnAgPC0gZ2FwbWluZGVyICU+JQogICAgZmlsdGVyKHllYXIgPT0gcGFzdF95ZWFyICYgY291bnRyeSAlaW4lIGNvdW50cnlfbGlzdCkgJT4lCiAgICBtdXRhdGUoZ3JvdXAgPSBpZmVsc2UocmVnaW9uICVpbiUgd2VzdCwgIldlc3QiLCAiRGV2ZWxvcGluZyIpKSAlPiUKICAgIGdncGxvdChhZXMoZG9sbGFyc19wZXJfZGF5LCB5ID0gLi5jb3VudC4uLCBmaWxsID0gZ3JvdXApKSArCiAgICBzY2FsZV94X2NvbnRpbnVvdXModHJhbnMgPSAibG9nMiIpCnAgKyBnZW9tX2RlbnNpdHkoYWxwaGEgPSAwLjIpICsgZmFjZXRfZ3JpZCh5ZWFyIH4gLikKYGBgCgpJbmNsdWRlIHRoZSBwcmVzZW50IHllYXIsIGFuZCB0aGVuIGFkZCBzbW9vdGhuZXNzIHdpdGggdGhlIGBid2AgYXJnIGluIGBhZXMoKWA6CgpgYGB7cn0KIyBzbW9vdGggZGVuc2l0eSBwbG90cyAtIHZhcmlhYmxlIGNvdW50cyBvbiB5LWF4aXMKcCA8LSBnYXBtaW5kZXIgJT4lCiAgICBmaWx0ZXIoeWVhciAlaW4lIGMocGFzdF95ZWFyLCBwcmVzZW50X3llYXIpICYgY291bnRyeSAlaW4lIGNvdW50cnlfbGlzdCkgJT4lCiAgICBtdXRhdGUoZ3JvdXAgPSBpZmVsc2UocmVnaW9uICVpbiUgd2VzdCwgIldlc3QiLCAiRGV2ZWxvcGluZyIpKSAlPiUKICAgIGdncGxvdChhZXMoZG9sbGFyc19wZXJfZGF5LCB5ID0gLi5jb3VudC4uLCBmaWxsID0gZ3JvdXApKSArCiAgICBzY2FsZV94X2NvbnRpbnVvdXModHJhbnMgPSAibG9nMiIpCnAgKyBnZW9tX2RlbnNpdHkoYWxwaGEgPSAwLjIsIGJ3ID0gMC43NSkgKyBmYWNldF9ncmlkKHllYXIgfiAuKQpgYGAKTGlrZSB3aXRoIHRoZSBgaWZlbHNlYCBmdW5jdGlvbiwgYGNhc2Vfd2hlbigpYCBjYW4gZGVmaW5lIHJlc3VsdHMgYmFzZWQgb24gY29uZGl0aW9uYWwgZXhwcmVzc2lvbnMsIGJ1dCBpcyBhYmxlIHRvIGhhdmUgbXVsdGlwbGUgb3B0aW9ucy4KCmBgYHtyfQojIGFkZCBncm91cCBhcyBhIGZhY3RvciwgZ3JvdXBpbmcgcmVnaW9ucwpnYXBtaW5kZXIgPC0gZ2FwbWluZGVyICU+JQogICAgbXV0YXRlKGdyb3VwID0gY2FzZV93aGVuKAogICAgICAgICAgICAuJHJlZ2lvbiAlaW4lIHdlc3QgfiAiV2VzdCIsCiAgICAgICAgICAgIC4kcmVnaW9uICVpbiUgYygiRWFzdGVybiBBc2lhIiwgIlNvdXRoLUVhc3Rlcm4gQXNpYSIpIH4gIkVhc3QgQXNpYSIsCiAgICAgICAgICAgIC4kcmVnaW9uICVpbiUgYygiQ2FyaWJiZWFuIiwgIkNlbnRyYWwgQW1lcmljYSIsICJTb3V0aCBBbWVyaWNhIikgfiAiTGF0aW4gQW1lcmljYSIsCiAgICAgICAgICAgIC4kY29udGluZW50ID09ICJBZnJpY2EiICYgLiRyZWdpb24gIT0gIk5vcnRoZXJuIEFmcmljYSIgfiAiU3ViLVNhaGFyYW4gQWZyaWNhIiwKICAgICAgICAgICAgVFJVRSB+ICJPdGhlcnMiKSkKCiMgcmVvcmRlciBmYWN0b3IgbGV2ZWxzCmdhcG1pbmRlciA8LSBnYXBtaW5kZXIgJT4lCiAgICBtdXRhdGUoZ3JvdXAgPSBmYWN0b3IoZ3JvdXAsIGxldmVscyA9IGMoIk90aGVycyIsICJMYXRpbiBBbWVyaWNhIiwgIkVhc3QgQXNpYSIsICJTdWItU2FoYXJhbiBBZnJpY2EiLCAiV2VzdCIpKSkKYGBgCgpUaGUgbmV3IGdyb3VwcyBjYW4gYmUgc2VwYXJhdGVkIGluIHRoZSBtYXBwaW5nLgoKYGBge3J9CiMgc21vb3RoIGRlbnNpdHkgcGxvdHMgLSB2YXJpYWJsZSBjb3VudHMgb24geS1heGlzCnAgPC0gZ2FwbWluZGVyICU+JQogICAgZmlsdGVyKHllYXIgJWluJSBjKHBhc3RfeWVhciwgcHJlc2VudF95ZWFyKSAmIGNvdW50cnkgJWluJSBjb3VudHJ5X2xpc3QpICU+JQogICAgZ2dwbG90KGFlcyhkb2xsYXJzX3Blcl9kYXksIHkgPSAuLmNvdW50Li4sIGZpbGwgPSBncm91cCkpICsKICAgIHNjYWxlX3hfY29udGludW91cyh0cmFucyA9ICJsb2cyIikKcCArIGdlb21fZGVuc2l0eShhbHBoYSA9IDAuMiwgYncgPSAwLjc1KSArIGZhY2V0X2dyaWQoeWVhciB+IC4pCmBgYAoKQSBzdGFja2VkIGRlbnNpdHkgcGxvdCBtYXkgYmUgYSBiZXR0ZXIgd2F5IHRvIGRpc3BsYXkgdGhlc2UgZ3JvdXBzLCBhcyBpdCB3aWxsIGJlIGxlc3MgY2x1dHRlcmVkLgoKYGBge3J9CiMgbm90ZSB5b3UgbXVzdCByZWRlZmluZSBwIHdpdGggdGhlIG5ldyBnYXBtaW5kZXIgb2JqZWN0IGZpcnN0CnAgPC0gZ2FwbWluZGVyICU+JQogIGZpbHRlcih5ZWFyICVpbiUgYyhwYXN0X3llYXIsIHByZXNlbnRfeWVhcikgJiBjb3VudHJ5ICVpbiUgY291bnRyeV9saXN0KSAlPiUKICAgIGdncGxvdChhZXMoZG9sbGFyc19wZXJfZGF5LCBmaWxsID0gZ3JvdXApKSArCiAgICBzY2FsZV94X2NvbnRpbnVvdXModHJhbnMgPSAibG9nMiIpCgojIHN0YWNrZWQgZGVuc2l0eSBwbG90CnAgKyBnZW9tX2RlbnNpdHkoYWxwaGEgPSAwLjIsIGJ3ID0gMC43NSwgcG9zaXRpb24gPSAic3RhY2siKSArCiAgICBmYWNldF9ncmlkKHllYXIgfiAuKQpgYGAKQSB3ZWlnaHRpbmcgY2FuIGJlIGRlZmluZWQgYXMgYSBuZXcgZmllbGQsIGFuZCB0aGVuIHRoZSBgd2VpZ2h0YCBhcmd1bWVudCBpbiB0aGUgbWFwcGluZyB3aWxsIG1ha2UgdGhlIGFkanVzdG1lbnRzIGZvciB0aGUgcGxvdC4KCmBgYHtyfQojIHdlaWdodGVkIHN0YWNrZWQgZGVuc2l0eSBwbG90CmdhcG1pbmRlciAlPiUKICAgIGZpbHRlcih5ZWFyICVpbiUgYyhwYXN0X3llYXIsIHByZXNlbnRfeWVhcikgJiBjb3VudHJ5ICVpbiUgY291bnRyeV9saXN0KSAlPiUKICAgIGdyb3VwX2J5KHllYXIpICU+JQogICAgbXV0YXRlKHdlaWdodCA9IHBvcHVsYXRpb24vc3VtKHBvcHVsYXRpb24qMikpICU+JQogICAgdW5ncm91cCgpICU+JQogICAgZ2dwbG90KGFlcyhkb2xsYXJzX3Blcl9kYXksIGZpbGwgPSBncm91cCwgd2VpZ2h0ID0gd2VpZ2h0KSkgKwogICAgc2NhbGVfeF9jb250aW51b3VzKHRyYW5zID0gImxvZzIiKSArCiAgICBnZW9tX2RlbnNpdHkoYWxwaGEgPSAwLjIsIGJ3ID0gMC43NSwgcG9zaXRpb24gPSAic3RhY2siKSArIGZhY2V0X2dyaWQoeWVhciB+IC4pCmBgYAojIyMgTG9naXN0aWMgVHJhbnNmb3JtYXRpb24KClRoZSBsb2dpc3RpYyBvciBsb2dpdCB0cmFuc2Zvcm1hdGlvbiBpcyBkZWZpbmVkIGFzIPCdkZMo8J2RnSk9bG9n8J2RnTHiiJLwnZGdLCBvciB0aGUgbG9nIG9mIG9kZHMuIFRoaXMgc2NhbGUgaXMgdXNlZnVsIGZvciBoaWdobGlnaHRpbmcgZGlmZmVyZW5jZXMgbmVhciAwIG9yIG5lYXIgMSBhbmQgY29udmVydHMgZm9sZCBjaGFuZ2VzIGludG8gY29uc3RhbnQgaW5jcmVhc2VzLgoKYGBge3J9CiMgZGVmaW5lIGdhcG1pbmRlcgpsaWJyYXJ5KHRpZHl2ZXJzZSkKbGlicmFyeShkc2xhYnMpCmRhdGEoZ2FwbWluZGVyKQoKIyBhZGQgYWRkaXRpb25hbCBjYXNlcwpnYXBtaW5kZXIgPC0gZ2FwbWluZGVyICU+JQogICAgbXV0YXRlKGdyb3VwID0gY2FzZV93aGVuKAogICAgICAgIC4kcmVnaW9uICVpbiUgd2VzdCB+ICJUaGUgV2VzdCIsCiAgICAgICAgLiRyZWdpb24gJWluJSAiTm9ydGhlcm4gQWZyaWNhIiB+ICJOb3J0aGVybiBBZnJpY2EiLAogICAgICAgIC4kcmVnaW9uICVpbiUgYygiRWFzdGVybiBBc2lhIiwgIlNvdXRoLUVhc3Rlcm4gQXNpYSIpIH4gIkVhc3QgQXNpYSIsCiAgICAgICAgLiRyZWdpb24gPT0gIlNvdXRoZXJuIEFzaWEiIH4gIlNvdXRoZXJuIEFzaWEiLAogICAgICAgIC4kcmVnaW9uICVpbiUgYygiQ2VudHJhbCBBbWVyaWNhIiwgIlNvdXRoIEFtZXJpY2EiLCAiQ2FyaWJiZWFuIikgfiAiTGF0aW4gQW1lcmljYSIsCiAgICAgICAgLiRjb250aW5lbnQgPT0gIkFmcmljYSIgJiAuJHJlZ2lvbiAhPSAiTm9ydGhlcm4gQWZyaWNhIiB+ICJTdWItU2FoYXJhbiBBZnJpY2EiLAogICAgICAgIC4kcmVnaW9uICVpbiUgYygiTWVsYW5lc2lhIiwgIk1pY3JvbmVzaWEiLCAiUG9seW5lc2lhIikgfiAiUGFjaWZpYyBJc2xhbmRzIikpCgojIGRlZmluZSBhIGRhdGEgZnJhbWUgd2l0aCBncm91cCBhdmVyYWdlIGluY29tZSBhbmQgYXZlcmFnZSBpbmZhbnQgc3Vydml2YWwgcmF0ZQpzdXJ2X2luY29tZSA8LSBnYXBtaW5kZXIgJT4lCiAgICBmaWx0ZXIoeWVhciAlaW4lIHByZXNlbnRfeWVhciAmICFpcy5uYShnZHApICYgIWlzLm5hKGluZmFudF9tb3J0YWxpdHkpICYgIWlzLm5hKGdyb3VwKSkgJT4lCiAgICBncm91cF9ieShncm91cCkgJT4lCiAgICBzdW1tYXJpemUoaW5jb21lID0gc3VtKGdkcCkvc3VtKHBvcHVsYXRpb24pLzM2NSwKICAgICAgICAgICAgICAgICAgICAgICAgaW5mYW50X3N1cnZpdmFsX3JhdGUgPSAxIC0gc3VtKGluZmFudF9tb3J0YWxpdHkvMTAwMCpwb3B1bGF0aW9uKS9zdW0ocG9wdWxhdGlvbikpCnN1cnZfaW5jb21lICU+JSBhcnJhbmdlKGluY29tZSkKCiMgcGxvdCBpbmZhbnQgc3Vydml2YWwgdmVyc3VzIGluY29tZSwgd2l0aCB0cmFuc2Zvcm1lZCBheGVzCnN1cnZfaW5jb21lICU+JSBnZ3Bsb3QoYWVzKGluY29tZSwgaW5mYW50X3N1cnZpdmFsX3JhdGUsIGxhYmVsID0gZ3JvdXAsIGNvbG9yID0gZ3JvdXApKSArCiAgICBzY2FsZV94X2NvbnRpbnVvdXModHJhbnMgPSAibG9nMiIsIGxpbWl0ID0gYygwLjI1LCAxNTApKSArCiAgICBzY2FsZV95X2NvbnRpbnVvdXModHJhbnMgPSAibG9naXQiLCBsaW1pdCA9IGMoMC44NzUsIC45OTgxKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgYnJlYWtzID0gYyguODUsIC45MCwgLjk1LCAuOTksIC45OTUsIC45OTgpKSArCiAgICBnZW9tX2xhYmVsKHNpemUgPSAzLCBzaG93LmxlZ2VuZCA9IEZBTFNFKQpgYGAKClRoZSBlY29sb2dpY2FsIGZhbGxhY3kgaXMgYXNzdW1pbmcgdGhhdCBjb25jbHVzaW9ucyBtYWRlIGZyb20gdGhlIGF2ZXJhZ2Ugb2YgYSBncm91cCBhcHBseSB0byBhbGwgbWVtYmVycyBvZiB0aGF0IGdyb3VwLgoKUmVtb3ZpbmcgdGhlIGdyb3VwaW5nLCBhbGwgdGhlIGRhdGEgaXMgc2hvd24gYWdhaW4uCgpgYGB7cn0KIyBwbG90IGluZmFudCBzdXJ2aXZhbCB2ZXJzdXMgaW5jb21lLCB3aXRoIHRyYW5zZm9ybWVkIGF4ZXMKZ2FwbWluZGVyICU+JSAKICBmaWx0ZXIoeWVhciAlaW4lIHByZXNlbnRfeWVhciAmICFpcy5uYShnZHApICYgIWlzLm5hKGluZmFudF9tb3J0YWxpdHkpICYgIWlzLm5hKGdyb3VwKSkgJT4lCiAgbXV0YXRlKGRvbGxhcnNfcGVyX2RheSA9IGdkcC9wb3B1bGF0aW9uLzM2NSkgJT4lCiAgICBnZ3Bsb3QoYWVzKGRvbGxhcnNfcGVyX2RheSwgMS1pbmZhbnRfbW9ydGFsaXR5LzEwMDAsIGNvbG9yID0gZ3JvdXApKSArCiAgc2NhbGVfeF9jb250aW51b3VzKHRyYW5zID0gImxvZzIiLCBsaW1pdCA9IGMoMC4yNSwgMTUwKSkgKwogIHNjYWxlX3lfY29udGludW91cyh0cmFucyA9ICJsb2dpdCIsIGxpbWl0ID0gYygwLjg3NSwgLjk5ODEpLAogICAgICAgICAgICAgICAgICAgICBicmVha3MgPSBjKC44NSwgLjkwLCAuOTUsIC45OSwgLjk5NSwgLjk5OCkpICsKICBnZW9tX3BvaW50KCkgCmBgYAo=